package com.easytouch.core;

import android.os.SystemClock;
import android.util.Pair;

import com.easytouch.core.encryption.Base64;
import com.easytouch.core.utils.Log;
import com.easytouch.core.utils.SystemTools;
import com.easytouch.core.utils.Tools;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;

/**
 * 长链接通信类
 * Created by gulin on 2017/7/20.
 */

public class PayLongThread {

    private static PayLongThread mPayLongThread;

    private static Log mLogger; //日志

    private static boolean isStopped = true;

    private Socket socket = null;
    private InputStream in = null;
    private OutputStream out = null;

    private ReadThread mReadThread = null;
    private WriteThread mWriteThread = null;
    private OutGoodsThread mOutGoodsThread = null;

    private static final int STATE_NONE = 0;
    private static final int STATE_SIGNING = 1;
    private static final int STATE_SIGNOK = 2;
    private static int state = STATE_NONE;

    private static final int RESULT_NO_ORDER = 0;
    private static final int RESULT_SUCCESS = 1;
    private static final int RESULT_FAIL = 2;

    private static LinkedList<String> mQueue = new LinkedList<String>(); // 主动指令队列

    private static ArrayList<OrderModel> mOrderList = new ArrayList<>(); //已经收到的订单号

    private static LinkedList<String> mOutGoodsQueue = new LinkedList<String>(); // 出货队列

    private String mHost = "easytouch-pay.cn"; //服务器地址

    private PayLongThread() {
        mLogger = new Log("PayLongThread.txt");
        mHost = Configure.getConfigData(Configure.CONFIG_PAY_IP);
        if (mHost == null || "".equals(mHost)) {
            mHost = "easytouch-pay.cn";
        }
        mWriteThread = new WriteThread();
        mWriteThread.start();
        mReadThread = new ReadThread();
        mReadThread.start();
        mOutGoodsThread = new OutGoodsThread();
        mOutGoodsThread.start();
    }

    public static PayLongThread getInstance() {
        if (mPayLongThread == null) {
            mPayLongThread = new PayLongThread();
        }
        return mPayLongThread;
    }

    private synchronized static void appendQueueData(String data) {
        //数据发送三次
        mQueue.offer(data);
        mQueue.offer(data);
        mQueue.offer(data);
    }

    private synchronized static String getQueueData() {
        return mQueue.poll();
    }

    private synchronized static int getQueueCount() {
        return mQueue.size();
    }

    private synchronized static void appendOutGoodsQueueData(String data) {
        //数据发送三次
        mOutGoodsQueue.offer(data);
        mOutGoodsQueue.offer(data);
        mOutGoodsQueue.offer(data);
    }

    private synchronized static String getOutGoodsQueueData() {
        return mOutGoodsQueue.poll();
    }

    private synchronized static int getOutGoodsQueueCount() {
        return mOutGoodsQueue.size();
    }

    private synchronized boolean isOrderInList(final String orderId) {
        for (OrderModel order : mOrderList)
        {
            if(order.order.equals(orderId))
            {
                return true;
            }
        }
        return false;
    }

    private synchronized void addOrderToList(final String orderId) {
        if(!isOrderInList(orderId))
        {
            mOrderList.add(new OrderModel(orderId, RESULT_NO_ORDER));
        }
    }

    private synchronized void checkAndDeleteOrder() {
        Iterator<OrderModel> sListIterator = mOrderList.iterator();
        while (sListIterator.hasNext()) {
            OrderModel e = sListIterator.next();
            if (!isOrderInTime(e.order, 1000 * 60 * 60)) {
                sListIterator.remove();
            }
        }
    }

    private synchronized void updateOrderResult(final String orderId, final int result)
    {
        for (OrderModel order : mOrderList)
        {
            if(order.order.equals(orderId))
            {
                order.result = result;
                return;
            }
        }
    }

    /**
     * 检查订单时间是否在time之内
     *
     * @param orderId 订单号
     * @param time    要判断的时间，单位毫秒
     * @return 是否在2分钟之内
     */
    private boolean isOrderInTime(final String orderId, final long time) {
        if (orderId == null || orderId.length() < 22) {
            return false;
        }
        String serverTime = orderId.substring(8, 22);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
        try {
            long serverTimeLong = sdf.parse(serverTime).getTime();
            if (Math.abs((new Date()).getTime() - serverTimeLong) > time) {
                return false;
            }
        } catch (ParseException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public void stopThread() {
        isStopped = true;
    }

    private void connectServer() throws IOException, SocketException, UnknownHostException {
        socket = new Socket();
        socket.setSoTimeout(1000 * 60 * 5);
        InetSocketAddress socAddress = new InetSocketAddress(mHost, 6002);
        socket.connect(socAddress, 30000);
        socket.setKeepAlive(true);
        in = socket.getInputStream();
        out = socket.getOutputStream();
    }

    private boolean isConnectSuccess() {
        try {
            connectServer();
        } catch (UnknownHostException e) {
            socket = null;
            in = null;
            out = null;
            mLogger.e("pay long " + e.getMessage());
            return false;
        } catch (SocketException e) {
            socket = null;
            in = null;
            out = null;
            mLogger.e("pay long " + e.getMessage());
            return false;
        } catch (IOException e) {
            socket = null;
            in = null;
            out = null;
            mLogger.e("pay long " + e.getMessage());
            return false;
        }
        return true;
    }

    private boolean checkConnect() {
        if (socket != null && in != null && out != null) {
            if (socket.isConnected()) {
                return true;
            } else {
                closeConnect();
                return false;
            }
        } else
            return false;
    }

    private boolean retryConnect() {
        return isConnectSuccess();
    }

    private synchronized void closeConnect() {
        try {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e1) {
        }
        socket = null;
        in = null;
        out = null;
    }

    private static boolean checkSign(final String data, final String sign) {
        if (data == null || "".equals(data)) {

            return false;
        }
        if (sign == null || "".equals(sign)) {
            return false;
        }
        if (sign.length() < 5) {
            return false;
        }
        String[] signArray = new String[5];
        signArray[0] = sign.substring(0, 1);
        signArray[1] = sign.substring(1, 2); //第二位
        signArray[2] = sign.substring(2, 4);
        signArray[3] = sign.substring(4, 5); //第五位
        signArray[4] = sign.substring(5);
        //交换第二位和第五位
        String temp = signArray[1];
        signArray[1] = signArray[3];
        signArray[3] = temp;
        //生成新的签名字符串
        String newSign = "";
        for (String s : signArray) {
            newSign += s;
        }
        mLogger.i("newSign: " + newSign);
        //Base64解密
        byte[] decodeBytes = Base64.decode(newSign);
        if (decodeBytes == null) {
            mLogger.e("base64 decode failed");
            return false;
        }
        String newData = new String(decodeBytes);
        //mLogger.i("decode:" + newData);
        mLogger.i("decode:" + newData);
        return newData.equals(data + "yichukeji2017");
//        String encode = sign(data);
//        mLogger.i("decode:" + encode);
//        return encode.equals(sign);
    }

    private static String sign(final String data) {
        if (data == null || "".equals(data)) {
            return null;
        }
        String newData = data + "yichukeji2017";
        String sign = Base64.encode(newData.getBytes());//MD5.md5Encode(data); //Base64.encode(newData.getBytes()); //Base64加密
        String[] signArray = new String[5];
        signArray[0] = sign.substring(0, 1);
        signArray[1] = sign.substring(1, 2); //第二位
        signArray[2] = sign.substring(2, 4);
        signArray[3] = sign.substring(4, 5); //第五位
        signArray[4] = sign.substring(5);
        //交换第二位和第五位
        String temp = signArray[1];
        signArray[1] = signArray[3];
        signArray[3] = temp;
        //生成新的签名字符串
        String newSign = "";
        for (String s : signArray) {
            newSign += s;
        }
        return newSign;
    }

    private class ReadThread extends Thread {
        private boolean run = true;

        @Override
        public void run() {
            super.run();

            while (run) {
                SystemClock.sleep(100);
                if (!checkConnect()) {
                    continue;
                }
                try {
                    if (in.available() <= 0) {
                        continue;
                    }
                    byte[] buf = new byte[2];
                    int readCount = in.read(buf);
                    if (readCount == -1) {
                        continue;
                    }
                    String sString = new String(buf);
                    if ("".equals(sString.trim())) {
                        throw new SocketTimeoutException();
                    }
                    if (sString.equals("00")) //签到和心跳的应答回应
                    {
                        if (state == STATE_SIGNING) {
                            //签到成功
                            state = STATE_SIGNOK;
                            mLogger.i("pay long sign ok");
                        } else {
                            mLogger.i("pay long heartbeat ok");
                        }
                    } else if (sString.equals("12")) //付款成功通知
                    {
                        handlePay(in);
                    } else if (sString.equals("13")) //出货失败应答回应
                    {
                        handleOutGoodsResult(in, false);
                    } else if (sString.equals("14")) //出货成功应答回应
                    {
                        handleOutGoodsResult(in, true);
                    } else if (sString.equals("18")) //长链接查询应答回应
                    {
                        handleQuery(in);
                    }
                } catch (SocketTimeoutException e) {
                    closeConnect();
                    mLogger.e("pay long timeout");
                } catch (IOException e) {
                    closeConnect();
                    mLogger.e("pay long IO error " + e.getMessage());
                } catch (Exception e) {
                    closeConnect();
                    mLogger.e("pay long data error " + e.getMessage());
                }
            }
        }

        /**
         * 处理支付成功请求
         *
         * @param in 输入流
         * @throws IOException IO异常
         */
        private void handlePay(InputStream in) throws IOException {
            //先读取长度
            byte[] buf = new byte[3];
            int r = in.read(buf);
            if (r == -1) {
                throw new IOException("pay long read 12 error");
            }
            String sString = new String(buf);
            mLogger.i("pay long 12 length:" + sString);
            if ("".equals(sString.trim())) {
                throw new SocketTimeoutException();
            }
            int len = 0;
            try {
                len = Integer.parseInt(sString);
            } catch (NumberFormatException e) {
                throw new IOException("pay long read 12 number format");
            }
            //获取数据
            buf = new byte[len];
            r = in.read(buf);
            int read = r;
            while (read < len) {
                if (r == -1) {
                    throw new IOException("pay long read 12 error");
                } else {
                    r = in.read(buf, read, len - read);
                    read += r;
                }
            }
            sString = new String(buf);
            mLogger.i("pay long 12 data:" + sString);
            appendOutGoodsQueueData(sString);
        }

        private void handleOutGoodsResult(InputStream in, boolean isOk) throws IOException {
            //获取数据
            int len = 27;
            byte[] buf = new byte[len];
            int r = in.read(buf);
            int read = r;
            while (read < len) {
                if (r == -1) {
                    throw new IOException("pay long read 13/14 error");
                } else {
                    r = in.read(buf, read, len - read);
                    read += r;
                }
            }
            String sString = new String(buf);
            mLogger.i("pay long 13/14 data:" + sString);
            //获取最后两位
            String result = sString.substring(sString.length() - 2);
            if (isOk) {
                if (result.equals("00")) {
                    mLogger.i("outGoods response ok");
                } else {
                    mLogger.i("outGoods response faild");
                }
            } else {
                if (result.equals("00")) {
                    mLogger.i("refund response ok");
                } else {
                    mLogger.i("refund response faild");
                }
            }
        }

        private void handleQuery(InputStream in) throws IOException {
            //先读取长度
            byte[] buf = new byte[3];
            int r = in.read(buf);
            if (r == -1) {
                throw new IOException("pay long read 18 error");
            }
            String sString = new String(buf);
            mLogger.i("pay long 18 length:" + sString);
            if ("".equals(sString.trim())) {
                throw new SocketTimeoutException();
            }
            int len;
            try {
                len = Integer.parseInt(sString);
            } catch (NumberFormatException e) {
                throw new IOException("pay long read 18 number format");
            }
            //获取数据
            buf = new byte[len];
            r = in.read(buf);
            int read = r;
            while (read < len) {
                if (r == -1) {
                    throw new IOException("pay long read 18 error");
                } else {
                    r = in.read(buf, read, len - read);
                    read += r;
                }
            }
            sString = new String(buf);
            mLogger.i("pay long 18 data:" + sString);
            //查询订单结果
            for (OrderModel order : mOrderList)
            {
                if(order.order.equals(sString))
                {
                    String data = sString + "_" + String.valueOf(order.result);
                    appendQueueData("20" + Tools.addLeftZero(String.valueOf(data.length()), 3) + data);
                    return;
                }
            }
            String data = sString + "_" + String.valueOf(RESULT_NO_ORDER);
            appendQueueData("20" + Tools.addLeftZero(String.valueOf(data.length()), 3) + data);
        }

        public void cancelThread() {
            run = false;
            this.interrupt();
        }

        public boolean isRunning() {
            return run;
        }
    }

    private class WriteThread extends Thread {
        private boolean run = true;

        private static final long HEARTBEAT_TIME = 1000 * 10;
        private long heartbeatLastTime = 0;

        @Override
        public void run() {
            super.run();

            while (run) {
                SystemClock.sleep(100);
                if (state == STATE_NONE) {
                    //签到
                    if (!checkConnect()) {
                        if (!retryConnect()) {
                            state = STATE_NONE;
                            mLogger.e("pay long retry connect failed");
                        } else {
                            if (!doSendSignData()) //发送签到数据
                            {
                                state = STATE_NONE;
                                mLogger.e("pay long sign failed");
                            }
                        }
                    } else {
                        if (!doSendSignData()) //发送签到数据
                        {
                            state = STATE_NONE;
                            mLogger.e("pay long sign failed");
                        }
                    }
                } else if (state == STATE_SIGNING) {
                    if (!checkConnect()) {
                        if (!retryConnect()) {
                            state = STATE_NONE;
                        }
                    }
                } else if (state == STATE_SIGNOK) {
                    //先检查是否队列中有要发送的数据
                    if (getQueueCount() > 0) {
                        if (!checkConnect()) {
                            if (!retryConnect()) {
                                mLogger.e("pay long retry connect failed");
                            } else {
                                String queueStr = getQueueData();
                                if (!doSendQueueData(queueStr)) {
                                    state = STATE_NONE;
                                    mLogger.e("pay long queue failed");
                                }
                            }
                        } else {
                            String queueStr = getQueueData();
                            if (!doSendQueueData(queueStr)) {
                                state = STATE_NONE;
                                mLogger.e("pay long queue failed");
                            }
                        }
                    } else {
                        //发送心跳
                        if (!checkConnect()) {
                            if (!retryConnect()) {
                                mLogger.e("pay long retry connect failed");
                            } else {
                                if (!doSendHeartBeatData()) {
                                    state = STATE_NONE;
                                    mLogger.e("pay long heartbeat failed");
                                }
                            }
                        } else {
                            if (!doSendHeartBeatData()) {
                                state = STATE_NONE;
                                mLogger.e("pay long heartbeat failed");
                            }
                        }
                    }
                }
            }
        }

        /**
         * 发送签到数据
         *
         * @return 是否成功
         */
        private boolean doSendSignData() {
            //签到
            state = STATE_SIGNING;
            int retry = 3;
            boolean isSuccess = false;
            while (retry > 0) {
                try {
                    //签到通讯协议：
                    //请求：10+8位机器号
                    //应答：00
                    String sign = "10" + Configure.getMacNo();
                    mLogger.i("pay long send sign:" + sign);
                    out.write(sign.getBytes());
                    out.flush();
                    isSuccess = true;
                } catch (SocketTimeoutException e) {
                    mLogger.e("pay long send sign timeout");
                } catch (IOException e) {
                    mLogger.e("pay long send sign IO error:" + e.getMessage());
                    break;
                } catch (Exception e) {
                    closeConnect();
                    mLogger.e("pay long send sign data error:" + e.getMessage());
                    break;
                }
                if (isSuccess) {
                    break;
                }
                retry--;
            }
            if (isSuccess) {
                SystemClock.sleep(100);
                return true;
            } else {
                closeConnect();
                return false;
            }
        }

        private boolean doSendQueueData(String queueStr) {
            int retry = 3;
            boolean isSuccess = false;
            while (retry > 0) {
                try {
                    //发送数据
                    mLogger.i("pay long send:" + queueStr);
                    out.write(queueStr.getBytes());
                    out.flush();
                    isSuccess = true;
                    heartbeatLastTime = System.currentTimeMillis();
                } catch (SocketTimeoutException e) {
                    mLogger.e("pay long send queue timeout");
                } catch (IOException e) {
                    mLogger.e("pay long send queue IO error:" + e.getMessage());
                    break;
                } catch (Exception e) {
                    closeConnect();
                    mLogger.e("pay long send queue data error:" + e.getMessage());
                    break;
                }
                if (isSuccess) {
                    break;
                }
                retry--;
            }
            if (isSuccess) {
                SystemClock.sleep(100);
                return true;
            } else {
                closeConnect();
                return false;
            }
        }

        private boolean doSendHeartBeatData() {
            long currentTime = System.currentTimeMillis();
            if (currentTime - heartbeatLastTime < HEARTBEAT_TIME) {
                return true;
            }
            heartbeatLastTime = currentTime;
            int retry = 3;
            boolean isSuccess = false;
            while (retry > 0) {
                try {
                    //心跳通讯协议
                    //请求：11+8位机器号
                    //应答：00
                    String sign = "11" + Configure.getMacNo();
                    mLogger.i("pay long send heartbeat:" + sign);
                    out.write(sign.getBytes());
                    out.flush();
                    isSuccess = true;
                } catch (SocketTimeoutException e) {
                    mLogger.e("pay long send heartbeat timeout");
                } catch (IOException e) {
                    mLogger.e("pay long send heartbeat IO error:" + e.getMessage());
                    break;
                } catch (Exception e) {
                    closeConnect();
                    mLogger.e("pay long send heartbeat data error:" + e.getMessage());
                    break;
                }
                if (isSuccess) {
                    break;
                }
                retry--;
            }
            if (isSuccess) {
                SystemClock.sleep(100);
                return true;
            } else {
                closeConnect();
                return false;
            }
        }

        public void cancelThread() {
            run = false;
            this.interrupt();
        }

        public boolean isRunning() {
            return run;
        }
    }

    class OrderModel
    {
        public String order = "";
        public int result = 0;

        public OrderModel()
        {
            this("", 0);
        }

        public OrderModel(String order, int result)
        {
            this.order = order;
            this.result = result;
        }
    }

    private class OutGoodsThread extends Thread {
        private boolean run = true;

        @Override
        public void run() {
            super.run();

            while (run) {
                SystemClock.sleep(1000);
                if(getOutGoodsQueueCount() > 0)
                {
                    String sString = getOutGoodsQueueData();
                    if(sString == null || "".equals(sString))
                    {
                        continue;
                    }
                    //验签
                    String data = sString.substring(0, 36);
                    String sign = sString.substring(36);
                    String orderId = data.substring(0, 25); //订单号 25位
                    String box = data.substring(25, 27); //柜号 2位
                    String column = data.substring(27, 29); //货道号 2位
                    String price = data.substring(29, 35); //价格 6位 单位分
                    String type = data.substring(35, 36); //支付方式 1位

                    if (checkSign(data, sign)) {
                        //验签成功
                        checkAndDeleteOrder();
                        if (isOrderInList(orderId)) {
                            continue;
                        }
                        //应答：12+订单号+2位标识（00：成功，01，验签失败）
                        appendQueueData("12" + orderId + "00");
                        //判断订单时间是否在2分钟内
                        if (isOrderInTime(orderId, 120000)) {
                            addOrderToList(orderId);
                            outGoods(orderId, box, column, price, type);
                        } else {
                            mLogger.i("order time out 2min");
                            //发送出货失败指令
                            sendOutGoodsResult(orderId, false);
                            if(!isOrderInTime(orderId, 3600000))
                            {
                                //订单时间超过一小时就重启工控机
                                SystemTools.reboot();
                            }
                        }
                    } else {
                        //验签失败
                        mLogger.e("pay long check sign failed");
                        //应答：12+订单号+2位标识（00：成功，01，验签失败）
                        appendQueueData("12" + orderId + "01");
                    }
                }
            }
        }

        private void outGoods(final String orderId, final String box, final String column, final String price, final String type) {
            int b, c, p;
            try {
                b = Integer.parseInt(box);
                c = Integer.parseInt(column);
                p = Integer.parseInt(price);
            } catch (NumberFormatException e) {
                mLogger.e("pay long outGoods number format error:" + e.toString());
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
                return;
            }
            String outGoodsType = DriverThread.OUT_GOODS_WEIXINPAY;
            if (type.equals("1")) {
                outGoodsType = DriverThread.OUT_GOODS_WEIXINPAY;
            } else if (type.equals("2")) {
                outGoodsType = DriverThread.OUT_GOODS_BAIPAY;
            } else if (type.equals("3")) {
                outGoodsType = DriverThread.OUT_GOODS_ALIPAY;
            } else if (type.equals("7")) {
                outGoodsType = DriverThread.OUT_GOODS_CCB;
            } else if (type.equals("8")) {
                outGoodsType = DriverThread.OUT_GOODS_SUNING;
            }else if(type.equals("e")){
                outGoodsType = DriverThread.OUT_GOODS_TRAVELCARD;
            } else if (type.equals("0")) {
                //充电请求
                requestCharge(orderId);
                return;
            }
            SimpleDateFormat sd = new SimpleDateFormat("HHmmss", Locale.US);
            String tradeTrace = sd.format(new Date());
            boolean result = DriverThread.getInstance().outGoods(b, c, p, outGoodsType, tradeTrace, orderId);
            if (!result) {
                mLogger.e("pay long outGoods result false");
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
                return;
            }
            //DriverThread.getInstance().addOrderId(tradeTrace, orderId);
            int step = 0;
            int r = -1;
            try {
                while (step <= 120) {
                    Thread.sleep(500);
                    step++;
                    Pair<Integer, Integer> pair = DriverThread.getInstance().getResult(tradeTrace);
                    if (pair != null) {
                        r = pair.first;
                        break;
                    }
                }
            } catch (InterruptedException e) {
            }
            mLogger.i("outGoods result:" + String.valueOf(r));
            if (r == 0) { // 出货成功
                //发送出货成功指令
                sendOutGoodsResult(orderId, true);
            } else { // 出货失败
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
            }
        }

        private void sendOutGoodsResult(final String orderId, final boolean isOk) {
            String sign = sign(orderId);
            if (sign == null) {
                mLogger.e("pay long sign failed");
                return;
            }
            updateOrderResult(orderId, isOk ? RESULT_SUCCESS : RESULT_FAIL);
            //出货失败
            //请求：13+3位长度（不包括13和3位长度本身）+订单号（25位）+验证签名
            //出货成功
            //请求：14+3位长度（不包括14和3位长度本身）+订单号（25位）+验证签名
            String data = isOk ? "14" : "13";
            data += Tools.addLeftZero(String.valueOf(orderId.length() + sign.length()), 3);
            data += orderId;
            data += sign;
            mLogger.i(data);
            appendQueueData(data);
        }

        private void requestCharge(final String orderId) {
            mLogger.i("charge request");
            //先从订单号里拿到线路
            char c = orderId.charAt(orderId.length() - 1);
            int line = c - '0';
            if (line < 1 || line > ChargeThread.MAX_LINE) {
                mLogger.e("charge request error: unknown line:" + c);
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
                return;
            }
            mLogger.i("charge request, line:" + String.valueOf(line));
            if (ChargeThread.getInstance().requestCharge(line)) {
                //发送出货成功指令
                sendOutGoodsResult(orderId, true);
            } else {
                //发送出货失败指令
                sendOutGoodsResult(orderId, false);
            }
        }

        public void cancelThread() {
            run = false;
            this.interrupt();
        }

        public boolean isRunning() {
            return run;
        }
    }
}
